package net.aiion.weave;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

import net.aiion.weave.asm.ClassVisitor;
import net.aiion.weave.asm.Label;
import net.aiion.weave.asm.MethodVisitor;
import net.aiion.weave.asm.Opcodes;
import net.aiion.weave.spi.cards.CardException;

public class CardForClass implements Opcodes
{
    ClassVisitor cw = null;
    MethodVisitor mv = null;

    public CardForClass() {
    }

    public static Collection<Integer> rnd() {
        Collection<Integer> randoms = new ArrayList<>();
        Random rnd = new Random();
        for (int i = 0; i < rnd.nextInt(); i++) {
            randoms.add(rnd.nextInt());
        }
        return randoms;
    }

    @SuppressWarnings("unchecked")
    public void doSomething() {
        Object o = rnd();
        Collection<Object> c = null;
        if (o instanceof Collection<?>) {
            c = (Collection<Object>) o;
        } else if (o.getClass().isArray()) {
            c = new ArrayList<>();
            int len = Array.getLength(o);
            for (int i = 0; i < len; i++) {
                c.add(Array.get(o, i));
            }
        } else {
            throw new CardException("Value '' is not an array or a collection.");
        }

        for (Object i : c) {
            System.out.println(i);
        }

        mv.visitMethodInsn(INVOKESTATIC, "net/aiion/weave/CardForClass", "rnd", "()Ljava/util/Collection;");
        mv.visitVarInsn(ASTORE, 1);
        mv.visitInsn(ACONST_NULL);
        mv.visitVarInsn(ASTORE, 2);

        // <<1> instanceof Collection
        // IFEQ <<l3>>
        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(INSTANCEOF, "java/util/Collection");
        Label l3 = new Label();
        mv.visitJumpInsn(IFEQ, l3);

        // <<2>> = (Collection) <<1>>
        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(CHECKCAST, "java/util/Collection");
        mv.visitVarInsn(ASTORE, 2);

        Label l5 = new Label();
        mv.visitJumpInsn(GOTO, l5);

        mv.visitLabel(l3);
        mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] {
            "java/lang/Object", "java/util/Collection"
        }, 0, null);
        // <<1>>.getClass().isArray()
        // IFEQ <<l6>>
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "isArray", "()Z");
        Label l6 = new Label();
        mv.visitJumpInsn(IFEQ, l6);

        // <<2>> = new ArrayList()
        mv.visitTypeInsn(NEW, "java/util/ArrayList");
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
        mv.visitVarInsn(ASTORE, 2);

        // <<3>> = Array.getLength(<<1>>)
        mv.visitVarInsn(ALOAD, 1);
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/reflect/Array", "getLength", "(Ljava/lang/Object;)I");
        mv.visitVarInsn(ISTORE, 3);

        // <<4>> = 0
        mv.visitInsn(ICONST_0);
        mv.visitVarInsn(ISTORE, 4);

        Label l10 = new Label();
        mv.visitJumpInsn(GOTO, l10);
        Label l11 = new Label();
        mv.visitLabel(l11);
        mv.visitFrame(Opcodes.F_APPEND, 2, new Object[] {
            Opcodes.INTEGER, Opcodes.INTEGER
        }, 0, null);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitVarInsn(ILOAD, 4);
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/reflect/Array", "get", "(Ljava/lang/Object;I)Ljava/lang/Object;");
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Collection", "add", "(Ljava/lang/Object;)Z");
        mv.visitInsn(POP);
        mv.visitIincInsn(4, 1);

        mv.visitLabel(l10);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitVarInsn(ILOAD, 4);
        mv.visitVarInsn(ILOAD, 3);
        mv.visitMethodInsn(INVOKESTATIC, "java/lang/reflect/Array", "getLength", "(Ljava/lang/Object;)I");
        mv.visitJumpInsn(IF_ICMPLT, l11);
        mv.visitJumpInsn(GOTO, l5);

        mv.visitLabel(l6);
        mv.visitFrame(Opcodes.F_CHOP, 1, null, 0, null);
        // throw new CardException("blah blah")
        mv.visitTypeInsn(NEW, "net/aiion/weave/cards/spi/CardException");
        mv.visitInsn(DUP);
        mv.visitLdcInsn("Value '' is not an array or a collection.");
        mv.visitMethodInsn(INVOKESPECIAL, "net/aiion/weave/cards/spi/CardException", "<init>", "(Ljava/lang/String;)V");
        mv.visitInsn(ATHROW);

        mv.visitLabel(l5);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitVarInsn(ALOAD, 2);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Collection", "iterator", "()Ljava/util/Iterator;");
        mv.visitVarInsn(ASTORE, 4);
        Label l14 = new Label();
        mv.visitJumpInsn(GOTO, l14);
        Label l15 = new Label();
        mv.visitLabel(l15);
        mv.visitFrame(Opcodes.F_FULL, 5, new Object[] {
            "net/aiion/weave/CardForClass", "java/lang/Object", "java/util/Collection", Opcodes.TOP,
            "java/util/Iterator"
        }, 0, new Object[] {});
        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
        mv.visitVarInsn(ASTORE, 3);

        // System.out.println(<<3>>)
        mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        mv.visitVarInsn(ALOAD, 3);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");

        mv.visitLabel(l14);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitVarInsn(ALOAD, 4);
        mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
        mv.visitJumpInsn(IFNE, l15);

        mv.visitInsn(RETURN);
        mv.visitMaxs(3, 5);
        mv.visitEnd();
    }
}
